<?php

/**
 * Mediad co,ltd
 *
 * @category   Mediad
 * @package    Mediad2_Service
 * @copyright  Copyright (c) 2012 Mediad co,ltd. (http://www.Mediad.cn)
 * @license    New BSD License
 * @version    $Id: Abstract.php 2012-02-02 11:19:00Z tany@Mediad.cn $
 */


/**
 * @category   Mediad
 * @package    Mediad2_Service
 */
abstract class Mediad2_Service_Abstract
{
	/**
	 * mapper object
	 *
	 * @var object of Mediad2_Mapper_Abstract
	 */
	protected $mapper;
	
	/**
	 * mapper class name
	 */
	protected $mapperClassName;
	
	/**
	 * model object
	 *
	 * @var Mediad2_Model_Abstract
	 */
	protected $model;
	
	/**
	 * model class name
	 */
	protected $modelClassName;
	
	/**
	 * result object
	 *
	 * @var Mediad2_Model_Result
	 */
	protected $result;
	
	/**
	 * helper object
	 * 
	 * @var Mediad2_Utility_SmartyHelper
	 */
	protected $helper;
	
	/**
	 * functions object
	 *
	 * @var Mediad2_Utility_Functions
	 */
	protected $functions;
	
	/**
	 * properties
	 * 
	 * @var array
	 */
    private $params = array(
    		"debug" => false,      // debug flag, default value is false
    	);   
	
	/**
	 * Constructor.
	 *
	 * @param array $params
	 * @return void
	 */
	public function __construct($params=null)
	{
		if (!empty($params) || count($params)>0) 
			$this->setParams($params);
		
		$this->result = new Mediad2_Model_Result();
		$this->helper = new Mediad2_Utility_SmartyHelper();
		$this->functions = new Mediad2_Utility_Functions();
		$this->init();
	}
	
	/**
	 * set params
	 * @param array $params
	 * @return void
	 */
	public function setParams($params=null)
	{
		if (isset($params['debug'])) $this->params['debug']=$params['debug'];
	}
	
	/**
	 * Initialize object
	 *
	 * Called from {@link __construct()} as final step of object instantiation.
	 *
	 * @return void
	 */
	public function init()
	{
	}
	
	/**
	 * get functions
	 *
	 * @return Mediad2_Utility_Functions
	 */
	public function getFunctions()
	{
		if (null === $this->functions) {
			throw new Mediad2_Service_Exception('没有定义functions!');
		}
		return $this->functions;
	}
	
	/**
	 * set mapper
	 *
	 * @param string $mapper
	 * @throws Exception
	 * @return Mediad2_Service_Abstract
	 */
	protected function setMapper($mapper)
	{
		if (is_string($mapper)) {
			$this->mapperClassName = $mapper;
			$m = new $mapper();
		}
		if (!$m instanceof Mediad2_Mapper_Abstract) {
			throw new Mediad2_Service_Exception('缺少类Mediad2_Mapper_Abstract的定义!');
		}
		$this->mapper = $m;
		return $this;
	}
	
	/**
	 * get mapper
	 *
	 * @return Mediad2_Mapper_Abstract
	 */
	public function getMapper()
	{
		if (null === $this->mapper) {
			throw new Mediad2_Service_Exception('没有定义mapper!');
		}
		return $this->mapper;
	}
	
	/**
	 * set model
	 *
	 * @param string $model
	 * @throws Exception
	 * @return Mediad2_Service_Abstract
	 */
	protected function setModel($model)
	{
		if (is_string($model)) {
			$this->modelClassName = $model;
			$m = new $model();
		}
		if (!$m instanceof Mediad2_Model_Abstract) {
			throw new Mediad2_Service_Exception('缺少类Mediad2_Model_Abstract的定义!');
		}
		$this->model = $m;
		return $this;
	}
	
	/**
	 * get model
	 *
	 * @return Mediad2_Model_Abstract
	 */
	public function getModel()
	{
		if (null === $this->model) {
			throw new Mediad2_Service_Exception('没有定义model!');
		}
		return $this->model;
	}
	
	/**
	 * set result
	 * 
	 * @param string $result
	 * @throws Exception
	 * @return Mediad2_Service_Abstract
	 */
	protected function setResult($result)
	{		
		if (is_string($result)) {
			$result = new $result();
		}

		if (!($result instanceof Mediad2_Model_Result)) {
			throw new Mediad2_Service_Exception('缺少类Mediad2_Model_Result!');
		}
		$this->result = $result;
		return $this;
	}
	
	/**
	 * get result
	 *
	 * @return Mediad2_Model_Result
	 */
	protected function getResult ()
	{
		if (null === $this->result) {
			$this->setResult('Mediad2_Model_Result');
		}
		return $this->result;
	}
	
	/**
	 * return $result
	 * 
	 * @param string $code
	 * @param string $message
	 * @param string $data
	 * @return Mediad2_Model_Result
	 */
	protected function returnResult($code, $message = null, $data = null)
	{
		$result = new Mediad2_Model_Result();
		
		if (!isset($message)) {
			switch ($code) {
				case Mediad2_Model_Result::CODE_SUCCESS:
					$message = Mediad2_Model_Result::MESSAGE_SUCCESS;
					break;
				case Mediad2_Model_Result::CODE_FAILURE:
					$message = Mediad2_Model_Result::MESSAGE_FAILURE;
					break;
				default:
					$message = '';
					break;
			}
		}
		if (!isset($data)) {
			$data = '';
		}
		
		$result->setCode($code);
		$result->setMessage($message);
		$result->setData($data);
		
		$this->setResult($result);
		return $this->result;
	}
	
	/**
	 * handle exception
	 * 
	 * @param Zend_Exception $e
	 * @return Mediad2_Service_Abstract
	 */
	protected function handleException(Zend_Exception $e, $debug = null)
	{
		if (empty($debug)) $debug = $this->params['debug'];
		if ($debug) {
			var_dump($e->getMessage());
			return $this;
		} else {
			//出错处理--log
			$registry = Zend_Registry::getInstance ();
			if ($registry->isRegistered('log')) {
				$log = $registry->get('log');
				$log->crit($e->getMessage(), $e);
			}
			//出错处理--邮件
			$em = new Mediad2_Service_Util_Email();
			$em->sendException($e->getMessage());
			return $this;
		}
	}
	
	/**
	 * echo 错误信息，然后退出
	 * @param string $message
	 */
	protected function handleErrorAndExit($message)
	{
		$result = $this->returnResult(Mediad2_Model_Result::CODE_FAILURE,$message);
		echo $this->encodeResult($result);
		exit(0);
	}
	
	/**
	 * insert a new row
	 * @param Mediad2_Model_Abstract $model
	 * @return Mediad2_Model_Result the data include mixed The primary key of the row inserted
	 * @throws Mediad2_Service_Exception 
	 */
	public function insert($model)
	{
		try {
		
			$mapper = $this->getMapper();
			$result = $mapper->insert($model->toArray());
		
			return $this->returnResult(Mediad2_Model_Result::CODE_SUCCESS, null, $result);
		
		} catch (Zend_Exception $e) {
		
			$this->handleException($e);
			return $this->returnResult(Mediad2_Model_Result::CODE_FAILURE, $e->getMessage());
		
		}
	}
	
	/**
	 * update by primary key
	 *
	 * @param Mediad2_Model_Abstract $model
	 * @return Mediad2_Model_Result the data include int The number of rows updated.
	 */
	public function update($model)
	{
		try {
		
			$mapper = $this->getMapper();
			$result = $mapper->update($model->toArray());
		
			return $this->returnResult(Mediad2_Model_Result::CODE_SUCCESS, null, $result);
		
		} catch (Zend_Exception $e) {
		
			$this->handleException($e);
			return $this->returnResult(Mediad2_Model_Result::CODE_FAILURE, $e->getMessage());
		
		}
	}
	
	
	/**
	 * delete by primary key
	 *
	 * @param int $id
	 * @return Mediad2_Model_Result the data include int The number of rows deleted.
	 */
	public function delete($id)
	{
		try {
			
			$mapper = $this->getMapper();
			$pk = $mapper->getPk();
			$parms = array("$pk[1]" => $id,);	//构造删除条件
			$result = $mapper->delete($parms);
		
			return $this->returnResult(Mediad2_Model_Result::CODE_SUCCESS, null, $result);
			
		} catch (Zend_Exception $e) {
			
			$this->handleException($e);			
			return $this->returnResult(Mediad2_Model_Result::CODE_FAILURE, $e->getMessage());
			
		}
	}
	
	/**
	 * delete by $where
	 *
	 * @param string $where
	 * @return Mediad2_Model_Result the data include int The number of rows deleted.
	 */
	public function deleteBy($where)
	{
		try {
			
			$mapper = $this->getMapper();
			$result = $mapper->deleteBy($where);
		
			return $this->returnResult(Mediad2_Model_Result::CODE_SUCCESS, null, $result);
			
		} catch (Zend_Exception $e) {
			
			$this->handleException($e);
			return $this->returnResult(Mediad2_Model_Result::CODE_FAILURE, $e->getMessage());
			
		}
	}
	
	/**
	 * fetch all
	 *
	 * @param int $pageIndex
	 * @param int $pageNum
	 * @param string $where
	 * @param array $order
	 * @param boolean $returnArray
	 * @return Mediad2_Model_Result the data include array row
	 */
	public function fetchAll($pageIndex = 1, $pageNum = 20, $where = null, $order = null, $returnArray = false , $group = null)
	{
		try {
		
			$mapper = $this->getMapper();
			$results = $mapper->fetchAll($pageIndex, $pageNum, $where, $order,$group);
			$models = array();
			foreach ($results as $key=>$value) {
				$model = new $this->modelClassName($value);
				if ($returnArray == true) {
					$models[] = $model->toArray();
				} else {
					$models[] = $model;
				}
			}

			return $this->returnResult(Mediad2_Model_Result::CODE_SUCCESS,null,$models);
		
		} catch (Zend_Exception $e) {
		
			$this->handleException($e);
			return $this->returnResult(Mediad2_Model_Result::CODE_FAILURE, $e->getMessage());
		
		}
	}
	
	public function fetchLimit($rowStart = 0, $rowEnd = 0, $where = null, $order = null, $returnArray = false)
	{
		try {
	
			$mapper = $this->getMapper();
			$results = $mapper->fetchLimit($rowStart, $rowEnd, $where, $order);
			$models = array();
			foreach ($results as $key=>$value) {
				$model = new $this->modelClassName($value);
				if ($returnArray == true) {
					$models[] = $model->toArray();
				} else {
					$models[] = $model;
				}
			}
	
			return $this->returnResult(Mediad2_Model_Result::CODE_SUCCESS,null,$models);
	
		} catch (Zend_Exception $e) {
	
			$this->handleException($e);
			return $this->returnResult(Mediad2_Model_Result::CODE_FAILURE, $e->getMessage());
	
		}
	}
	
	/**
	 * get count by conditions
	 *
	 * @param string $condition
	 * @return Mediad2_Model_Result the data include int The number of rows
	 */
	public function getCount($condition = null,$group = null)
	{
		try {
			
			$mapper = $this->getMapper();
			$result = $mapper->getCount($condition,$group);
			return $this->returnResult(Mediad2_Model_Result::CODE_SUCCESS, null, $result);
			
		} catch (Zend_Exception $e) {
			
			$this->handleException($e);
			return $this->returnResult(Mediad2_Model_Result::CODE_FAILURE, $e->getMessage());
			
		}
		
	}
	
	/**
	 * find by id , id is primary key
	 *
	 * @param int $id
	 * @return Mediad2_Model_Result
	 */
	public function find($id,$returnArray = false)
	{
		try {
			
			$mapper = $this->getMapper();
			
			$pk = $mapper->getPk();			
			$parms = array("$pk[1]" => $id);
			$result = $mapper->find($parms);
			
			$model = $this->getModel();
			if (!null == $result) {
				$model->setOptions($result->toArray());
			}
			if($returnArray){//create by vincent 20130724
				$model = $model->toArray();
			}
		
			return $this->returnResult(Mediad2_Model_Result::CODE_SUCCESS,null,$model);
		
		} catch (Zend_Exception $e) {
		
			$this->handleException($e);
			return $this->returnResult(Mediad2_Model_Result::CODE_FAILURE, $e->getMessage());
		
		}
		
	}
	
	/**
	 * find by $where
	 *
	 * @param string $where
	 * @param array $order
	 * @return Mediad2_Model_Result the data include row
	 */
	public function findBy($where, $order = null,$returnArray = false)
	{
		
		try {
		
			$mapper = $this->getMapper();
			$result = $mapper->findBy($where, $order);
		
			$model = $this->getModel();
			if (!null == $result) {
				$model->setOptions($result->toArray());
			}
			if($returnArray){
				$model = $model->toArray();
			}
			return $this->returnResult(Mediad2_Model_Result::CODE_SUCCESS,null,$model);
		
		} catch (Zend_Exception $e) {
		
			$this->handleException($e);
			return $this->returnResult(Mediad2_Model_Result::CODE_FAILURE, $e->getMessage());
		}
		
	}
	
	/**
	 * 根据条件，返回记录的指定字段的信息
	 *
	 * @param string $condition
	 * @param array $fields
	 * @param array $order
	 * @return Mediad2_Model_Result the data include row
	 */
	public function getFieldsBy($condition, $fields = null, $order = null)
	{
		try {
		
			$mapper = $this->getMapper();
			$row = $mapper->findBy($condition, $order, $fields);
			if (!empty($row)) {
				return $this->returnResult(Mediad2_Model_Result::CODE_SUCCESS,null,$row->toArray());
			} else {
				return $this->returnResult(Mediad2_Model_Result::CODE_SUCCESS,null,null);
			}
		
		} catch (Zend_Exception $e) {
		
			$this->handleException($e);
			return $this->returnResult(Mediad2_Model_Result::CODE_FAILURE, $e->getMessage());
		}
	}
	
	/**
	 * fill model 将参数中的数组数据填充到model，并进行验证
	 * @param array $data
	 * @return Mediad2_Model_Result
	 */
	public function fillModel(array $data)
	{
		//var
		$mapper = $this->getMapper();
		$primary = $mapper->getPk();
		$pk = $primary[1]; //主键数组从1开始
		$metadata = $mapper->getMetadata();
		
		//获取所有字段名称，类型，非空属性
		$meta = array();
		foreach($metadata as $key=>$val) {//获取所有字段名称，类型，非空属性
			if ($val['PRIMARY']) continue;
			if ($val['NULLABLE'] == true) {
				$isnull = 1;
			} else {
				$isnull = 0;
			}
			$meta[] = array("name"=>$key,"type"=>$val['DATA_TYPE'],"isnull"=>$isnull);
		}
		
		//根据传入的参数数组，对应赋值
		foreach ($data as $key=>$var) {
			foreach ($meta as $index => $value) {
				if ($key == $meta[$index]['name']) {
					if (!isset($data[$key])) continue; //必填字段必须有值且不能为空字符串
					$meta[$index]['data'] = $data[$key];
				}
			}
		}
		
		//验证
		$arrModel = array();
		foreach ($meta as $key => $value) {
			$arg = @$meta[$key]['data'];
			$name = @$meta[$key]['name'];
			//判断必填项
			if ( $meta[$key]['isnull']==0 && !isset($arg) ) {
				return $this->returnResult(Mediad2_Model_Result::CODE_FAILURE, "$name is not null");
			}
			$arrModel[$name]= $arg;
		}
		
		//如果存在PK值，将PK值保存在model中
		$obj_pk = @$data[$pk];
		if(isset($obj_pk)){
			$arrModel[$pk] = $obj_pk;
		}
		
		$this->model->setOptions($arrModel);
		return $this->returnResult(Mediad2_Model_Result::CODE_SUCCESS,null,$this->model);
		
	}
	
	/**
	 * save model 保存fillmodel方法中返回的model
	 * @param Mediad2_Model_Abstract $model
	 * @return Mediad2_Model_Result
	 */
	public function saveModel(Mediad2_Model_Abstract $model,$fn = null)
	{
		//fn 1:insert 2:update by vincent
		if(!isset($fn)){
			$fn = "insert";
		}
		
		//var
		$mapper = $this->getMapper();
		$primary = $mapper->getPk();
		$pk = $primary[1]; //主键数组从1开始
		$metadata = $mapper->getMetadata();
		
		//verify pk
		if(!isset($pk))
			return $this->returnResult(Mediad2_Model_Result::CODE_FAILURE, "pk is null");
		
		//将传入的数据替换到update模板中,并保存更新
		$source 	= $model->toArray();
		
		if ($fn == "update") {
			$objResult 	= $this->find($source[$pk]);
			if ($objResult->getCode()==Mediad2_Model_Result::CODE_FAILURE) {
				return $this->returnResult(Mediad2_Model_Result::CODE_FAILURE);
			}
			$objmodel 	= $objResult->getData();
			$target 	= $objmodel->toArray();
			foreach ($source as $key => $value) {
				$target[$key] = $source [$key];
			}
			$objmodel = $this->model->setOptions($target);
			return $this->update($objmodel);
		} else {
			$objmodel 	= new $this->modelClassName();
			$target 	= $objmodel->toArray();
			foreach ($source as $key => $value) {
				$target[$key] = $source[$key];
				if ($key == $pk) unset($target[$key]);
			}
			$objmodel->setOptions($target);
			return $this->insert($objmodel);
		}
	}
		
}